import {FC} from './fc.js'
import utils from './utils.js'

// canvas尺寸
var SCREEN_WIDTH = 256;
var SCREEN_HEIGHT = 240;

// patternCanvas尺寸
var PATTERN_WIDTH = 128;
var PATTERN_HEIGHT = 128;

// canvas变量
var canvas_ctx = null
var image = null
var buffer = null
var framebuffer_u8 = null
var framebuffer_u32 = null
var frameCount = 0

// patternCanvas变量
var patternCanvasCtx = null
var patternImage = null
var patternBuffer = null
var pattern_framebuffer_u8 = null
var pattern_framebuffer_u32 = null

// 音频变量
var SAMPLE_RATE = 44100
// 音频buffer的大筐，模拟器向其中写数据，音频api从这里读数据，大小初步设计为4096
var audioProcessBuffer = new Float32Array(4096)

var audioContext = null
var audioReadPos = 0

// 创建jsfc
var jsfc = new FC()

var lastFrameTime = null
var debugMode = false

window.onload = function(){
    loadNesFile()
	initCanvas()
	initAudio()
}

// 加载rom
function loadNesFile(){
    var req = new XMLHttpRequest()
	req.open("GET", './roms/kong.nes')
	req.overrideMimeType("text/plain; charset=x-user-defined")
	req.onload = function() {
		if (this.status === 200) {
		    reset(this.responseText)
		} else if (this.status === 0) {
			// Aborted, so ignore error
		} else {
			// req.onerror();
		}
	}
	req.send()
}

// 初始化canvas
function initCanvas(){
	// 1.初始化canvas
	var canvas = document.getElementById('jsfc_canvas')
	canvas_ctx = canvas.getContext('2d')
	// canvas初始化为黑色
	canvas_ctx.fillStyle = '0xFFFFFF'
	canvas_ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
	// getImageData() 方法返回 ImageData 对象,其中保存了画布指定矩形的像素数据
	// 每个像素数据包含RGBA信息，以数组形式存在，并存储于 ImageData 对象的 data 属性中
	// 256*240个像素点，每个像素点包含4Byte的RGBA数据，因此image的长度应为256*240*4=245760
	image = canvas_ctx.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
	// buffer长度为245760
	buffer = new ArrayBuffer(image.data.length)
	// 从buffer引用出一个Uint8ClampedArray类型的数组来操作二进制数据，这个数组用来更新canvas
	framebuffer_u8 = new Uint8ClampedArray(buffer)
	// 从buffer引用出一个32位无符号整数数组，用来传递到jsfc来设置ARGB数据，它和framebuffer_u8其实指向同一个buffer，只是分割方式不同
	framebuffer_u32 = new Uint32Array(buffer)
	// 2.初始化patternTableCanvas
	var patternCanvas = document.getElementById('pattern_canvas')
	patternCanvasCtx = patternCanvas.getContext('2d')
	// canvas初始化为黑色
	patternCanvasCtx.fillStyle = '0xFFFFFF'
	patternCanvasCtx.fillRect(0, 0, PATTERN_WIDTH, PATTERN_HEIGHT)
	// getImageData() 方法返回 ImageData 对象,其中保存了画布指定矩形的像素数据
	// 每个像素数据包含RGBA信息，以数组形式存在，并存储于 ImageData 对象的 data 属性中
	// 256*240个像素点，每个像素点包含4Byte的RGBA数据，因此image的长度应为256*240*4=245760
	// patternImage = patternCanvas.getImageData(0, 0, PATTERN_WIDTH, PATTERN_HEIGHT)
	// buffer长度为245760
	// patternBuffer = new ArrayBuffer(patternImage.data.length)
	// pattern_framebuffer_u8 = new Uint8ClampedArray(patternBuffer)
	// pattern_framebuffer_u32 = new Uint32Array(patternBuffer)
}

// 初始化音频
function initAudio(){
	audioContext = new AudioContext()
	console.log("sampleRate:"+audioContext.sampleRate)
	// createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels)
	// 缓冲区大小为4096，每秒约调用10次audiocallback
	// 缓冲区设计为512，，如果采样率为44100，每秒约调用86次
	// 相当于每帧充入700，然后定时取出500，在1s之后基本同步
	var scriptProcessor = audioContext.createScriptProcessor(512,0,1)
	scriptProcessor.onaudioprocess = audioCallback
	scriptProcessor.connect(audioContext.destination);
}

// 音频回调函数
function audioCallback(event){
    var outputBuffer = event.outputBuffer
    var outputBufferLength = outputBuffer.length
    var channelData = outputBuffer.getChannelData(0)
    // buffer里的数据都处理完了，需要从大buffer里取数据
	for(var i = 0; i < outputBufferLength; i++){
		// 0xFFF = 4095 ,将写入位置和它相与，可以让写入位置在0-4095间循环
		channelData[i] = audioProcessBuffer[(this.audioReadPos + i) & 0xFFF]
	}
	// 读取缓存后，需改已读取的位置，如果超过长度，则循环
	this.audioReadPos = (this.audioReadPos + outputBuffer.length) & 0xFFF
	// console.log('当前读取的缓存位置为：'+this.audioReadPos)
	// // 以下随机生成噪音
	// for(var i = 0; i < outputBufferLength; i++){
	// 	channelData[i] = Math.random() * 2 - 1
	// }
    // console.log('audioCallback')
    // console.log('time:'+frameStartTime)
}

// 加载rom后复位模拟器
function reset(nesData){
	// 将网页加载的nesData进行转化为buffer流，否则后台的rom无法识别并getRomData
	var data = new Uint8Array(new ArrayBuffer(nesData.length))
	for (var i = 0, il = nesData.length; i < il; i++) {
		var value = nesData.charCodeAt(i)
		data[i] = value
	}
	// 复位模拟器时传入前台的buffer变量，将显示和模拟器关联起来
	// 同时传入音频变量
    jsfc.reset(data,framebuffer_u32,debugMode,audioProcessBuffer)
	// 绑定浏览器刷新时间，用来定时从后台获取数据刷新canvas
	window.requestAnimationFrame(onAnimationFrame)
	// lastFrameTime = new Date()
}

// 浏览器定时刷新时间，默认60帧，约16ms一帧
function onAnimationFrame(){
	// var currentFrameTime = new Date()
	// var frameInterval = currentFrameTime - lastFrameTime
	// lastFrameTime = currentFrameTime
	// console.log('调用onAnimationFrame,间隔时间:'+ frameInterval +' ms')
	frameCount++
	window.requestAnimationFrame(onAnimationFrame);
	// 每帧调用jsfc的方法来刷新buffer
	jsfc.onFrame()
	// 刷新buffer后将buffer数据填充到image.data中，用来刷新canvas
	image.data.set(framebuffer_u8)
	canvas_ctx.putImageData(image, 0, 0)
	// patternImage.data.set(pattern_framebuffer_u8)
	// patternCanvasCtx.putImageData(patternImage, 0, 0)
}

// 绑定键盘事件
addEventListener('keydown',function(event){
	jsfc.bindKey(event.key,'press')
})

addEventListener('keyup',function(event){
	jsfc.bindKey(event.key,'release')
})

// function toFormattedHex(num,length){
// 	var len = num.toString(16).length
// 	var outStr = num.toString(16).toUpperCase()
// 	while(len < length) {
// 		outStr = '0' + outStr
// 		len++
// 	}
// 	return outStr
// }



// window.requestAnimationFrame(drawFrame)

// function drawFrame(){
//     window.requestAnimationFrame(drawFrame)
// }

// ES6模式需要通过window绑定html事件
window.setDebugMode = function(){
	var radioValue = ""
	var radio = document.getElementsByName("debugMode")
	for(var i=0;i<radio.length;i++){
		if(radio[i].checked){
			radioValue = radio[i].value
		}
	}
	if(radioValue == "off"){
		debugMode = false
	}
	else if(radioValue == "on"){
		debugMode = true
	}
	jsfc.debugMode = debugMode
}

window.runByFrame = function(){
	jsfc.runByFrame()
}

window.reload = function(){
	loadNesFile()
	initCanvas()
}

window.showDebugStatus = function(){
	var radioValue = ""
	var radio = document.getElementsByName("debugMode")
	for(var i=0;i<radio.length;i++){
		if(radio[i].checked){
			radioValue = radio[i].value
		}
	}
	var debugInfo = jsfc.debugInfo
	var pc = utils.toFormattedHex(debugInfo.pc,4)
	document.getElementById("pc").value = pc
	var sp = utils.toFormattedHex(debugInfo.sp,2)
	document.getElementById("sp").value = sp
	var ps = utils.toFormattedHex(debugInfo.ps,2)
	document.getElementById("ps").value = ps
	var memory = debugInfo.memory
	var ta = '';
	var memoryAddress = 0x0000
	ta += utils.toFormattedHex(memoryAddress,4) + '    '
	for(let i=0;i<memory.length;i++){
		// ta += memory[i] + ' '
		ta += utils.toFormattedHex(memory[i],2) + ' '
		if((i+1) % 16 == 0){
			ta += '\n'
			memoryAddress += 0x10
			ta += utils.toFormattedHex(memoryAddress,4) + '    '
		}
	}
	document.getElementById("memory").value = ta
}

window.runByStep = function(){
	jsfc.runByStep()
}
